package com.king.pages.main;

import org.kordamp.ikonli.javafx.FontIcon;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import com.king.SpringInitOk;
import com.king.components.info.ExceptionInfoController;
import com.king.event.ExceptionEvent;
import com.king.event.FormDialogEvent;
import com.king.event.LoadingEvent;
import com.king.event.NotificationEvent;
import com.king.pages.novel.NovelPage;

import atlantafx.base.controls.ModalPane;
import atlantafx.base.controls.RingProgressIndicator;
import atlantafx.base.util.Animations;
import javafx.animation.PauseTransition;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.geometry.Side;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.MenuBar;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.util.Duration;
import lombok.extern.slf4j.Slf4j;
import net.rgielen.fxweaver.core.FxControllerAndView;
import net.rgielen.fxweaver.core.FxWeaver;
import net.rgielen.fxweaver.core.FxmlView;

/**
 * 主窗口-controller FxmlView注解注入的FXML文件，如果不写路径，那么须与此类在同一路径下
 * 
 * @author king
 * @date 2025年6月18日-16:18:07
 */
@Slf4j
@FxmlView
@Component
public class MainWin {

	@Autowired
	private SpringInitOk app;

	@FXML
	private HBox headerBox;

	@FXML
	private StackPane body;

	@FXML
	private VBox notificationBox;

	@FXML
	private MenuBar headerBar;

	@FXML
	private AnchorPane mainLeft;

	@FXML
	private AnchorPane mainRight;

	@FXML
	private Button minimizeBtn;

	@FXML
	private Button maximizeBtn;

	@FXML
	private Button closeBtn;

	@FXML
	private FontIcon maxBtn;

	@Autowired
	private FxWeaver fxWeaver;

	// main窗口的body中添加一个遮罩
	private ModalPane infoModal;

	/**
	 * 记录当前的加载状态
	 */
	private LoadingEvent loading;

	/**
	 * 接收到加载时的时间毫秒数
	 */
	private long loadingStartTime;

	/**
	 * 加载延迟多久触发
	 */
	private boolean showLoading;

	/**
	 * 加载中的模态框
	 */
	private ModalPane loadModal;

	/**
	 * 表单模态框
	 */
	private ModalPane formPane;

	@FXML
	private void initialize() {
		app.dragWin(headerBar);
		infoModal = new ModalPane(-20);
		formPane = new ModalPane(-10);
		loadModal = new ModalPane(-100);
		body.getChildren().addAll(loadModal, infoModal, formPane);
		loading = LoadingEvent.CLOSE;
		showLoading = false;
	}

	@FXML
	private void loadBook() {
		changeRightNode(fxWeaver.loadView(NovelPage.class));
	}

	/**
	 * 修改右侧窗体的元素，且右侧窗体元素须是VBox作为根
	 * 
	 * @param box
	 * @author king
	 * @date 2025年6月23日-16:10:11
	 */
	private void changeRightNode(VBox box) {
		mainRight.getChildren().clear();
		box.prefWidthProperty().bind(mainRight.widthProperty());
		box.prefHeightProperty().bind(mainRight.heightProperty());
		mainRight.getChildren().add(box);
	}

	@FXML
	private void close() throws Exception {
		Platform.exit();
	}

	@FXML
	private void minimize() {
		app.minimize();
	}

	@FXML
	private void maximize() {
		app.maximize();
	}

	/**
	 * spring的事件通知监听，写到这里，其实我们很熟悉了，因为启动Application后，我们在ini中启动了spring的上下文，然后做了一个事件发布
	 * 
	 * 
	 * @param e
	 * @author king
	 * @date 2025年6月27日-16:12:19
	 */
	@Async
	@EventListener
	private void infoListener(ExceptionEvent e) {
		// 这里处理异常的弹框UI
		FxControllerAndView<ExceptionInfoController, Node> c = fxWeaver.load(ExceptionInfoController.class);
		ExceptionInfoController ctrl = c.getController();
		ctrl.setContent(e.getMsg());
		// 定义信息显示框只有窗口的一般大小，从右侧弹出
		ctrl.setWidth(app.getStageWidth() / 2);
		// 将模态窗口放置在右侧
		infoModal.usePredefinedTransitionFactories(Side.RIGHT);
		infoModal.setAlignment(Pos.TOP_RIGHT);
		c.getView().ifPresent((view) -> {
			Platform.runLater(() -> {
				infoModal.show(view);
			});
		});
	}

	/**
	 * 监听加载事件
	 * 
	 * @param e
	 * @author king
	 * @date 2025年7月4日-15:32:16
	 */
	@EventListener
	private void loadListener(LoadingEvent e) {
		// 如果当前状态就是通知的，那么就不用执行，如：本来当前状态就是loading中，那么再次发来一个loading，那么就不用处理了
		if (loading.equals(e)) {
			log.info("阻止状态：{}", e);
			return;
		}
		log.info("loading:{}", e);
		// 记录状态
		if (LoadingEvent.LOADING.equals(e)) {
			// 如果是运行状态，那么记录时间
			loadingStartTime = System.currentTimeMillis();
		}
		// 延迟100毫秒在执行是否要显示加载动画
		byte duration = 100;
		if (LoadingEvent.CLOSE.equals(e)) {
			// 如果loading与close的时间间隔小于100，即PauseTransition的延迟执行时间，那么就直接不处理动画了
			showLoading = System.currentTimeMillis() - loadingStartTime > duration;
			log.info("showLoading:{}", showLoading);
		}
		// 优化，处理如果发布加载和关闭的间隙太短，就没有必要处理加载动画了，避免屏幕一闪的不良用户体验
		if (LoadingEvent.LOADING.equals(e)) {
			PauseTransition delay = new PauseTransition(Duration.millis(duration));
			delay.setOnFinished((_) -> {
				if (!showLoading) {
					log.info("loading和close间隔太短，不处理");
					return;
				}
				// 执行到此处，说明showLoading是true了，待下次处理，所以这里此值必须还原成false
				showLoading = false;
				log.info("-----------------------running");
				// 如果是加载
				Text text = new Text("加载中...");
				RingProgressIndicator ring = new RingProgressIndicator(-1D, false);
				ring.setMinSize(200, 200);
				StackPane box = new StackPane();
				box.getChildren().addAll(text, ring);

				loadModal.show(box);
			});
			delay.play();
		} else {
			// 关闭加载动画，清空模态框内的内容
			// 设置成false，避免如果异步的同时，如：delay到时间了执行，然后此时计算的showLoading是true，然后关闭先执行了，然后在执行到show，那么就会显示loading了
			showLoading = false;
			if (loadModal.isDisplay()) {
				Platform.runLater(() -> {
					// show是放在动画中的，动画必然是在渲染线程中执行的，所以不用担心可能无法渲染的问题，但是hide就不敢保证了，所以必须说动处理
					loadModal.hide();
				});
			}
		}
		loading = e;
	}

	/**
	 * 弹框监听，主要是表单这种弹框的监听
	 * 
	 * @param e
	 * @author king
	 * @date 2025年7月4日-16:30:35
	 */
	@EventListener
	private void formListener(FormDialogEvent e) {
		Platform.runLater(() -> {
			Region node = e.getNode();
			if (node == null) {
				formPane.hide();
				return;
			}

			double width = e.getWidth();
			if (width < 1 && width > 0) {
				width = app.getStageWidth() * e.getWidth();
			}
			node.setMinWidth(width);
			node.setMaxWidth(width);
			node.setPrefWidth(width);
			formPane.setPersistent(e.isEscape());
			formPane.setAlignment(Pos.TOP_RIGHT);
			formPane.usePredefinedTransitionFactories(Side.RIGHT);
			formPane.show(node);
			e.getNode().requestFocus();
		});
	}

	/**
	 * 通知事件-监听
	 * 
	 * @param e
	 * @author king
	 * @date 2025年7月16日-11:15:51
	 */
	@EventListener
	private void notificationListener(NotificationEvent e) {
		// 做一个显示动画，当然，要将这个组件添加到窗口
		notificationBox.getChildren().add(e);
		Timeline t = Animations.fadeIn(e, Duration.seconds(e.getDuration()));
		t.setOnFinished((_) -> {
			if (e.isAutoClose()) {
				// 如果是自动关闭的，定义10秒后自动关闭
				PauseTransition p = new PauseTransition(Duration.seconds(10));
				p.setOnFinished((_) -> {
					e.getOnClose().handle(null);
				});
				p.play();
			}
		});

		t.play();

		e.setOnClose((_) -> {
			Timeline tc = Animations.fadeOut(e, Duration.seconds(e.getDuration()));
			tc.setOnFinished((_) -> notificationBox.getChildren().remove(e));
			tc.play();
		});
	}

}
